package com.techshinogongjian.fp.comm;

import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;

import com.techshinogongjian.fingerprint.FPConfig;
import com.techshinogongjian.fingerprint.TCFP;
import com.fingerUsb.FingerResult;
import com.techshinogongjian.fp.util.FpCoder;
import com.techshinogongjian.fp.util.FpConst;
import com.techshinogongjian.fp.util.FpImage;
import com.techshinogongjian.fp.util.Logs;

/**
 * HID协议
 * 
 * @author wangzhi
 */
public class HIDComm extends IComm {

	private static final String TAG = HIDComm.class.getSimpleName();

	private static final int MAX_USBFS_BUFFER_SIZE = 16384;

	private static final int RECV_SIZE = 64;

	private UsbDeviceConnection mConn;
	private UsbEndpoint mInEndpoint;
	private UsbEndpoint mOutEndpoint;

	private int mImgSize;
	private int mWidth;
	private int mHeight;
	private int mDPI;

	// 命令行发送缓冲区
	private byte[] bSendBuf = null;
	private int nSendDatLen = 0;
	// 接收缓冲区
	private byte[] bRecvBuf = null;
	private int nRecvDatLen = 0;
	private byte[] bImg;
	private byte[] bImgHead;

	private int nTimeoutDoFeature = 18000; // 取验证指纹的超时限定
	private int nTimeoutDoComm = 5000; // 单个指令通信超时限定
	private int nTimeoutDoTemplt = 32000; // 去注册模版的超时限定

	private int mWriteTimeout = 4000;
	private boolean isInitOk = false;
	private MsgCallback mCallback;

	public HIDComm(UsbDeviceConnection usbDeviceConnection,
			UsbEndpoint inEndpoint, UsbEndpoint outEndpoint) {
		mConn = usbDeviceConnection;
		mInEndpoint = inEndpoint;
		mOutEndpoint = outEndpoint;

		bSendBuf = new byte[1024];
		bRecvBuf = new byte[1024];
	}

	public int getRecvDatLen() {
		return nRecvDatLen;
	}

	public byte[] getbRecvBuf() {
		return bRecvBuf;
	}

	public void setInitOk(boolean isInitOk) {
		this.isInitOk = isInitOk;
	}

	public boolean isInitOk() {
		return isInitOk;
	}

	@Override
	public int read(byte[] pbReadOutBuf, int nStartPos, int nWantReadLen) {
		return read(pbReadOutBuf, nStartPos, nWantReadLen, nTimeoutDoComm);
	}

	private int read(byte[] pbReadOutBuf, int nStartPos, int nWantReadLen,
			int timeout) {
		int nRet = mConn.bulkTransfer(mInEndpoint, pbReadOutBuf, nWantReadLen,
				timeout);
		return nRet;
	}

	@Override
	public int write(byte[] data) {
		int nRet = mConn.bulkTransfer(mOutEndpoint, data, data.length,
				mWriteTimeout);
		return nRet;
	}

	@Override
	public boolean isCancel() {
		return nCancelFlag == 1;
	}

	public void setCancel() {
		nCancelFlag = 1;
	}

	private int doCommand(int cmd) {
		int nRet = 0;
		byte[] bytes = getSendBytes();
		bytes[2] = (byte) cmd;

		readClear();
		nRet = write(bytes);
		Logs.i(TAG, "nRet:" + nRet);
		return nRet;
	}

	public int recvCmd() {
		int nRet = read(bRecvBuf, 0, nRecvDatLen);
		Logs.i(TAG, "read1 ret:" + nRet);
		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}
		nRecvDatLen = nRet;
		// nRet = read(new byte[RECV_SIZE], 0, RECV_SIZE);
		return nRet;
	}

	/**
	 * 获取图片属性指令
	 */
	private int spCmdDevFpHDR() {
		int nRet;
		nRecvDatLen = RECV_SIZE;
		nRet = doCommand(FpConst.HID_VRSN);

		nRet = recvCmd();

		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}

		// 解析图片属性
		bImgHead = new byte[28];
		byte[] sizeBytes = new byte[4];
		byte[] widthBytes = new byte[2];
		byte[] heightBytes = new byte[2];
		byte[] dpiBytes = new byte[2];

		System.arraycopy(bRecvBuf, 20, bImgHead, 0, 28);
		System.arraycopy(bRecvBuf, 26, widthBytes, 0, 2);
		System.arraycopy(bRecvBuf, 28, heightBytes, 0, 2);
		System.arraycopy(bRecvBuf, 30, dpiBytes, 0, 2);
		System.arraycopy(bRecvBuf, 40, sizeBytes, 0, 4);
		mImgSize = FpCoder.fourByteToInt(sizeBytes);
		mWidth = FpCoder.twoByteToInt(widthBytes);
		mHeight = FpCoder.twoByteToInt(heightBytes);
		mDPI = FpCoder.twoByteToInt(dpiBytes);
		Logs.i(TAG, "size:" + mImgSize + " DPI:" + mDPI + " width:" + mWidth
				+ " height:" + mHeight);

		if (mImgSize < 0 || mImgSize > 100000 || mWidth < 0 || mHeight < 0) {
			nRet = FpConst.TCY_ERRO;
		}

		return nRet;
	}

	public boolean isConnected() {
		return spCmdDevFpHDR() >= 0;
	}

	/**
	 * 获取图像指令
	 * 
	 * @return
	 */
	private int spCmdDevImg() {
		// 获取图片属性
		int nRet = spCmdDevFpHDR();
		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}

		if (bImg == null) {
			bImg = new byte[mImgSize];
		}
		nRecvDatLen = mImgSize;
		int readSize = 0;

		// 发送获取指纹图像指令
		nRet = doCommand(FpConst.HID_RIMG);

		// 读取指纹图像
		byte[] bTempImg = new byte[MAX_USBFS_BUFFER_SIZE];
		int index = 0;
		while ((nRet = read(bTempImg, 0, MAX_USBFS_BUFFER_SIZE)) > 0) {
			int remain = (readSize + nRet) > mImgSize ? mImgSize - readSize
					: nRet;

			Logs.d(TAG, "remain:" + remain);
			if (index == 0) {
				System.arraycopy(bTempImg, RECV_SIZE, bImg, readSize, remain
						- RECV_SIZE);
				readSize += nRet - RECV_SIZE;
			} else {
				System.arraycopy(bTempImg, 0, bImg, readSize, remain);
				readSize += nRet;
			}
			index++;

			if (readSize >= mImgSize) {
				break;
			}
		}

		Logs.i(TAG, "nRet:" + nRet);

		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}
		nRecvDatLen = readSize;

		return nRet;
	}

	/**
	 * 检测手指按捺
	 * 
	 * @return
	 */
	private int spCmdPres() {
		int nRet;
		nRecvDatLen = RECV_SIZE;
		nRet = doCommand(FpConst.HID_PRES);
		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}
		nRet = recvCmd();
		HexOut(TAG, bRecvBuf, 0, 64);

		Logs.i(TAG, "nRet:" + nRet + " bRecvBuf[13]==0 " + (bRecvBuf[13] < 10)
				+ " bRecvBuf[13]=" + bRecvBuf[13]);
		int variance = bRecvBuf[12] & 0xff;// 方差
		int pres = bRecvBuf[13] & 0xff;// 有效面积率
		int graylevel = bRecvBuf[14] & 0xff;//平均灰度
		
//		Logs.d(TAG,"方差：" + variance + " 面积率：" + pres + " 平均灰度：" + graylevel);
		
		if (nRet >= 0 && graylevel < 124) {
			nRet = FpConst.TCY_NOFP;
		}

		return nRet;
	}

	/**
	 * 用户友好提示
	 * 
	 * @param val
	 *            0=短嘀，1=长嘀，2=嘀嘀
	 * @return
	 */
	private int spCmdHint(int val) {
		return doCommand(FpConst.HID_CTRL);
	}

	/**
	 * 获取sn命令
	 * 
	 * @return
	 */
	private int spCmdDevSn() {
		int nRet;
		nRecvDatLen = RECV_SIZE;
		nRet = doCommand(FpConst.HID_RDSN);
		if (nRet < 0) {
			return FpConst.TCY_NLNK;
		}
		nRet = recvCmd();

		return nRet;
	}

	/**
	 * read 方法必须确保可以读取到正确长度的数据，否则应该返回错误
	 */
	private int readClear() {
		int nRet = FpConst.TCY_NLNK;
		byte[] bytes = new byte[256];
		nRet = read(bytes, 0, 256, 200);
		return nRet;
	}

	private byte[] getSendBytes() {
		byte[] bytes = new byte[64];
		bytes[0] = 0x21;
		bytes[1] = 0x00;

		return bytes;
	}

	private void HexOut(String tag, byte[] bOut, int nOffSet, int nLen) {
		Logs.HexOut(tag, bOut, nOffSet, nLen);
	}

	/**
	 * 获取设备info
	 * 
	 * @return
	 */
	public FingerResult getDevInfo() {
		Logs.i(TAG, "getDevInfo...");
		FingerResult result = new FingerResult(FpConst.TCY_SUCC);
		int nRet = 0;

		return result;
	}

	/**
	 * 获取设备sn
	 * 
	 * @return
	 */
	public FingerResult getDevSn() {
		Logs.i(TAG, "getDevSn...");
		FingerResult result = new FingerResult(FpConst.TCY_SUCC);
		int nRet = 0;
		String sSn = "";
		nRet = spCmdDevSn();
		if (nRet < 0) {
			return result.setStatus(FpConst.TCY_NLNK);
		}
		byte[] bSn = new byte[nRecvDatLen];
		System.arraycopy(bRecvBuf, 12, bSn, 0, nRecvDatLen - 12);
		sSn = new String(bSn);
		result.setStatus(nRet);
		result.setResult(sSn.trim());
		return result;
	}

	public int devGetImg(int nTimeout) {
		Logs.d(TAG, "devGetImg Enter.");
		isFuncTimeout(nTimeout);
		boolean isTimeout = false;
		int nRet = 0;

		while (!(isTimeout = isFuncTimeout())) {

			// 取消
			if (nCancelFlag == 1) {
				nCancelFlag = 0;
				return FpConst.TCY_CNCL;
			}
			// 交易超时
			if (isFuncTimeout() == true) {
				return FpConst.TCY_TMOT;
			}
			nRet = spCmdPres();
			Logs.i(TAG, "isTimeout:" + isTimeout + " nRet:" + nRet);
			if (nRet < 0) {
				if (nRet == FpConst.TCY_NOFP) {
					continue;
				}
				isFuncTimeout(0);
				return nRet;
			} else {
				nRet = spCmdDevImg();
				if (nRet < 0) {
					return FpConst.TCY_NLNK;
				}
				break;
			}
		}

		if (isTimeout) {
			return FpConst.TCY_TMOT;
		}

		isFuncTimeout(0);
		return nRet;
	}

	/**
	 * 获取指纹图像
	 * 
	 * @return
	 */
	public FingerResult tcGetImg(int nTimeout) {
		Logs.d(TAG, "tcGetImg Enter.");
		FingerResult result = new FingerResult();
		
		if (!isInitOk) {
			return result.setStatus(FpConst.DOG_NONE);
		}
		
		int nRet = 0;

		nRet = devGetImg(nTimeout);
		if (nRet < 0) {
			return result.setStatus(nRet);
		}

		Logs.i(TAG, "tcGetImg:" + nRet);
		FpImage fpImage = new FpImage();
		fpImage.saveRawAsBmpBuf(bImg, mWidth, mHeight);
		result.setImgBytes(fpImage.getBmpImgBuf());
		result.setStatus(nRet);

		// spCmdHint(0);
		return result;
	}

	/**
	 * 获取指纹特征
	 * 
	 * @param timeout
	 * @return
	 */
	public FingerResult tcGetFeature(int nTimeout) {
		Logs.d(TAG, "tcGetFeature Enter.");
		FingerResult result = new FingerResult();
		int nRet = devGetImg(nTimeout);

		if (nRet < 0) {
			return result.setStatus(nRet);
		}

		result.setStatus(nRet);
		int[] imageAttr = new int[3];
		imageAttr[0] = mWidth; // width
		imageAttr[1] = mHeight; // height
		imageAttr[2] = mDPI; // resolution

		byte[] feature = new byte[512];
		if (FPConfig.getFeatureType() < 5) {
			byte[] imgAndHead = new byte[28 + bImg.length];
			System.arraycopy(bImgHead, 0, imgAndHead, 0, 28);
			System.arraycopy(bImg, 0, imgAndHead, 28, bImg.length);
			nRet = TCFP.TCFP_FeatureExtract(feature, imgAndHead,
					imgAndHead.length, imageAttr);
		} else {
			nRet = TCFP.TCFP_FeatureExtract(feature, bImg, bImg.length,
					imageAttr);
		}

		Logs.i(TAG, "feature length:" + nRet);

		if (nRet < 0) {
			return result.setStatus(nRet);
		}

		result.setTemplate(feature);
		FpImage fpImage = new FpImage();
		fpImage.saveRawAsBmpBuf(bImg, mWidth, mHeight);
		result.setImgBytes(fpImage.getBmpImgBuf());

		return result;
	}

	/**
	 * 注册指纹
	 * 
	 * @param timeout
	 * @param msgCallback
	 * @return
	 */
	public FingerResult tcEnroll(int timeout, MsgCallback msgCallback) {
		mCallback = msgCallback;
		long start = System.currentTimeMillis();
		FingerResult result = null;
		byte[] feature1 = null;
		byte[] feature2 = null;
		byte[] feature3 = null;

		int nRet = FpConst.TCY_NOFP;
		int i = 0;
		while (true) {
			if (System.currentTimeMillis() - start > timeout) {
				return new FingerResult(FpConst.TCY_TMOT);
			}
			if (isCancel()) {
				nCancelFlag = 0;
				return new FingerResult(FpConst.TCY_CNCL);
			}
			if (nRet != FpConst.TCY_NOFP) {
				sendMsg(FpConst.TCY_TOLV, null);
				nRet = spCmdPres();
				continue;
			}

			sendMsg(FpConst.TCY_CAPTUREIMAGE, null);

			result = tcGetFeature(3000);

			if (result.getStatus() >= 0) {
				spCmdHint(0);
				if (i == 0) {
					feature1 = result.getTemplate();
				} else if (i == 1) {
					feature2 = result.getTemplate();
				} else if (i == 2) {
					feature3 = result.getTemplate();
				}
				i++;
				sendMsg(FpConst.TCY_GETIMAGE, result.getImgBytes());
				nRet = spCmdPres();
			} else if (result.getStatus() == FpConst.TCY_CNCL) {
				return new FingerResult(FpConst.TCY_CNCL);
			}

			if (i > 2) {
				byte[] template = new byte[512];

				int code = TCFP.TCFP_FeatureTempletExtract(feature1, feature2,
						feature3, template);
				Logs.i(TAG, "合成模板：" + code);
				if (code < 0) {
					return new FingerResult(FpConst.TCY_NSAM);
				}
				result.setTemplate(template);

				return result;
			}
		}
	}

	private void sendMsg(int code, byte[] img) {
		if (mCallback != null) {
			mCallback.call(code, img);
		}
	}
}
